package logic;

import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;

import common.Fictionator;
import common.interfaces.ConfigurationsInterface;
import common.interfaces.DBInterface;
import common.interfaces.StatisticsInterface;
import common.models.StatisticsModel;
import common.types.FictionalCharacter;
import common.types.Pair;
import common.types.StatisticsEntry;
import common.utils.CommonUtils;

public class DataLogic implements StatisticsInterface, ConfigurationsInterface {
	
	public DBInterface getDBAccess() {
		return Fictionator.getInstance().getDBAccess();
	}

	@Override
	public StatisticsModel getCurrentStatistics() throws Exception {
		Pair<SortedSet<StatisticsEntry>, Integer> statisticsHistory = getDBAccess().queryStatisticsHistory();
		SortedSet<StatisticsEntry> history = statisticsHistory.getLeft();
		StatisticsModel model = new StatisticsModel();
		
		model.setSuccesfulGuesses(statisticsHistory.getRight());
		model.setTotalGuesses(history.size());
		List<Pair<String,String>> gameHistory = new ArrayList<Pair<String,String>>();
		Map<String,Integer> mostPlayed = new HashMap<String, Integer>();
		
		for (StatisticsEntry statisticsEntry : history) {
			Pair<String, String> pair = new Pair<String, String>(statisticsEntry.getActualCharacterName(), statisticsEntry.getGuessCharacterName());
			gameHistory.add(pair);
			int times = 1;
			if (mostPlayed.containsKey(statisticsEntry.getActualCharacterName())) {
				times += mostPlayed.get(statisticsEntry.getActualCharacterName());
			} 
			mostPlayed.put(statisticsEntry.getActualCharacterName(), times);
		}
		
		model.setGameHistory(gameHistory);
		model.setMostPlayed(takeBestResults(mostPlayed, 10));
		return model;
	}

	private class ReverseComparator implements Comparator<Integer> {
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
	}
	
	private Map<String, Integer> takeBestResults(Map<String, Integer> mostPlayed, int limit) {
	    Map<String, Integer> result = new HashMap<String, Integer>();
	    Map<Integer,Set<String>> sortedBestResults = new TreeMap<Integer, Set<String>>(new ReverseComparator());
	    
	    for (Entry<String, Integer> entry : mostPlayed.entrySet()) {
	        if (!sortedBestResults.containsKey(entry.getValue())) {
	            Set<String> set = new HashSet<String>();
	            sortedBestResults.put(entry.getValue(), set);
	        }
	        sortedBestResults.get(entry.getValue()).add(entry.getKey());
	    }
	    
	    Iterator<Entry<Integer, Set<String>>> iterator = sortedBestResults.entrySet().iterator();
	    Entry<Integer, Set<String>> entry;
	    int i = 0;
	    while (i<limit && iterator.hasNext()) {
	        entry = iterator.next();
	        Iterator<String> names = entry.getValue().iterator();
	        while (i<limit && names.hasNext()) {
	            String name = names.next();
	            result.put(name, entry.getKey());
	            i++;
	        }
 	    }
	    
	    return result;
	}
	
	@Override
	public boolean createCharacter(FictionalCharacter character) throws Exception {
		return getDBAccess().createCharacter(character);
	}

	@Override
	public Pair<List<FictionalCharacter>, Integer> getCharactersByName(String name, int limit) throws Exception {
		return getDBAccess().queryCharacter(name,limit);
	}

	@Override
	public boolean removeCharacter(FictionalCharacter character) throws Exception {
		return getDBAccess().removeCharacter(character.getCharacterID());
	}

	@Override
	public boolean updateCharacter(FictionalCharacter character) throws Exception {
		return getDBAccess().updateCharacter(character);
	}

    @Override
    public boolean importToDB(int guiSessionID, boolean onlineImport, File freeBaseArchive) throws Exception {
        boolean authorized = false;
        File freeBaseRoot = null;
        try {
            authorized = getDBAccess().requestEditMode(guiSessionID);
            if (!authorized) {
                return false;
            }
            
            if (onlineImport) {
                freeBaseRoot = CommonUtils.downloadAndExtractLatestDatadump(".");
            } else {
                freeBaseRoot = CommonUtils.extractDataDump(".", freeBaseArchive, false);
            }
            
            if (freeBaseRoot == null) {
                return false;
            }
            
            return getDBAccess().importToDB(freeBaseRoot);
        } finally {
            if (authorized) {
                getDBAccess().endEditMode(guiSessionID);
            }
            if (freeBaseRoot != null) {
                CommonUtils.deleteDirectory(freeBaseRoot);
            }
        }
    }

    @Override
    public void closeResources() throws Exception {
        getDBAccess().close();
    }

    @Override
    public void forceGuiSessionCleanup(int guiSessionID) throws Exception {
        getDBAccess().forceGuiSessionCleanup(guiSessionID);
    }

    @Override
    public void cleanGUISession(int guiSessionID) throws Exception {
        getDBAccess().cleanGUISession(guiSessionID);
    }

}
